package com.xiao.singleapp.utils;

import com.xiao.singleapp.domain.ColumnDO;
import com.xiao.singleapp.domain.TableDO;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 代码生成器工具类
 *
 * @CreateBy 花和尚  1130365579@qq.com  2023/3/19 16:13
 * @Version 1.0
 */
public class GenCodeUtil {

//    /**
//     * 表操作记录配置
//     */
//    private static class TableOperaField {
//        // 表数据行操作记录
//        String createById;
//        String createByName;
//        String createDataTime;
//        String modifyById;
//        String modifyByName;
//        String modifyDataTime;
//        String deleteById;
//        String deleteByName;
//        String deleteByDataTime;
//
//        // 逻辑删除字段
//        String logicalDeletion;
//    }


    private static final String JDBC_TYPE = "JDBC_TYPE_";
    private static Configuration vmConfig = null;
//    private static TableOperaField tableOperaField = null;

    /*
     * 初始化静态变量
     */
    static {
        // 获取模板相关配置
        try {
            vmConfig = getGenConfig();
//            tableOperaField = new TableOperaField();
//            // 表造作记录
//            tableOperaField.createById = vmConfig.getString("createById");
//            tableOperaField.createByName = vmConfig.getString("createByName");
//            tableOperaField.createDataTime = vmConfig.getString("createDataTime");
//            tableOperaField.modifyById = vmConfig.getString("modifyById");
//            tableOperaField.modifyByName = vmConfig.getString("modifyByName");
//            tableOperaField.modifyDataTime = vmConfig.getString("modifyDataTime");
//            tableOperaField.deleteById = vmConfig.getString("deleteById");
//            tableOperaField.deleteByName = vmConfig.getString("deleteByName");
//            tableOperaField.deleteByDataTime = vmConfig.getString("deleteByDataTime");
//            // 逻辑删除字段
//            tableOperaField.logicalDeletion = vmConfig.getString("logicalDeletion");
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }
    }

    // 0. 加载所有模板路径
    public static List<String> loadTemplates() {
        ArrayList<String> templates = new ArrayList<>();
        templates.add("readGenerDir/Controller.java.vm");
        templates.add("readGenerDir/DaoInterface.java.vm");
        templates.add("readGenerDir/DaoMapper.xml.vm");
        templates.add("readGenerDir/Domain.java.vm");
        templates.add("readGenerDir/ServiceImpl.java.vm");
        templates.add("readGenerDir/ServiceInterface.java.vm");
        return templates;
    }


    /**
     * 根据表信息生成代码并打包成 zip 包（单表）
     *
     * @param tableInfo   单表信息
     * @param columnsInfo 表字段信息
     * @param zip         zip输出流 生成的代码文件传到 此 zip 输出流
     * @param packages    包名 从开始包名到业务模块 例如 com.xiao.xxxbiz
     */
    public static void generatorCode(Map<String, String> tableInfo, List<Map<String, String>> columnsInfo,
                                     ZipOutputStream zip, String packages) {
        // 组装表信息
        TableDO table = new TableDO();
        table.setTableName(tableInfo.get("TABLE_NAME"));
        table.setTableComment(tableInfo.get("TABLE_COMMENT"));

        // 表名转化成 java类名称
        String className = tableNameToJavaClassName(table.getTableName(), vmConfig.getString("tabPrefix"),
                vmConfig.getString("autoRemovePre"));
        table.setClassName(className);

        // 字符串第一个字母转换为小写， 当该类作为其他类的属性时使用
        table.setClassname(StringUtils.uncapitalize(className));

        boolean hasBigDecimal = false;
        List<ColumnDO> columnDOList = new ArrayList<>();
        for (Map<String, String> column : columnsInfo) {
            ColumnDO columnDO = new ColumnDO();
            columnDO.setColumnName(column.get("COLUMN_NAME"));
            String dataType = column.get("DATA_TYPE");
            if ("decimal".equalsIgnoreCase(dataType)) {
                hasBigDecimal = true;
            }
            columnDO.setDataType(dataType);
            columnDO.setExtra("");
            String attrName = strToJavaClass(columnDO.getColumnName());
            columnDO.setAttrName(attrName);
            columnDO.setAttrname(StringUtils.uncapitalize(attrName));
            String columnType = column.get("COLUMN_TYPE");
            if ("int(11)".equals(columnType)) {
                columnDO.setAttrType("Long");
            } else if ("bigint(1)".equals(columnType)) {
                columnDO.setAttrType("Integer");
            } else {
                //列的数据类型，转换成Java类型  未知类型是 以 "unknownType" 填充
                String attrType = vmConfig.getString(columnDO.getDataType(), "unknownType");
                columnDO.setAttrType(attrType);
            }
            String jdbcType = vmConfig.getString(JDBC_TYPE + column.get("DATA_TYPE"), "unknownType");
            columnDO.setJdbcType(jdbcType);
            columnDO.setColumnComment(column.get("COLUMN_COMMENT"));
            //是否主键
            if (!strIsEmptyNotTrim(column.get("COLUMN_KEY")) && "PRI".equalsIgnoreCase(column.get("COLUMN_KEY"))
                    && table.getPkColumn() == null) {
                // 设为表的主键
                table.setPkColumn(columnDO);
            }
            columnDOList.add(columnDO);
        }
        // 组装表字段列表
        table.setColumnDOList(columnDOList);
        // 如果没有主键则设置第一个字段为主键
        if (table.getPkColumn() == null) table.setPkColumn(columnDOList.get(0));

        //设置 Velocity 模板引擎资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        // 封装模板数据
        packages = strIsEmptyNotTrim(packages) ? vmConfig.getString("package") : packages;
        Map<String, Object> map = new HashMap<>(16);
        map.put("tableName", table.getTableName());
        map.put("comments", table.getTableComment());
        map.put("pk", table.getPkColumn());
        map.put("className", table.getClassName());
        map.put("classname", table.getClassname());
        map.put("pathName", packages.substring(packages.lastIndexOf(".") + 1));
        map.put("columns", table.getColumnDOList());
        map.put("package", packages);
        map.put("author", vmConfig.getString("author"));
        map.put("email", vmConfig.getString("email"));
        map.put("hasBigDecimal", hasBigDecimal);
        map.put("datetime", parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date()));
        // 表操作记录字段 含 逻辑删除字段   这里put 模板内比较时比较结果错误，
//        map.put("createById ",  vmConfig.getString("createById"));
//        map.put("createByName ",  vmConfig.getString("createByName"));
//        map.put("createDataTime ",  vmConfig.getString("createDataTime"));
//        map.put("modifyById ",  vmConfig.getString("modifyById"));
//        map.put("modifyByName ",  vmConfig.getString("modifyByName"));
//        map.put("modifyDataTime ",  vmConfig.getString("modifyDataTime"));
//        map.put("deleteById ",  vmConfig.getString("deleteById"));
//        map.put("deleteByName ",  vmConfig.getString("deleteByName"));
//        map.put("deleteByDataTime ",  vmConfig.getString("deleteByDataTime"));
//        map.put("logicalDeletion", vmConfig.getString("logicalDeletion"));
        // 是否使用 swagger
        map.put("isUseSwagger", Boolean.valueOf(vmConfig.getString("isUseSwagger")));
        map.put("isUseLombok", Boolean.valueOf(vmConfig.getString("isUseLombok")));
        // 基础继承配置
        map.put("isExtendsBaseEntry", Boolean.valueOf(vmConfig.getString("isExtendsBaseEntry")));
        map.put("domainExtends", vmConfig.getString("domainExtends"));
        map.put("domainExtendsSimpleName", vmConfig.getString("domainExtendsSimpleName"));
        map.put("isExtendsController", Boolean.valueOf(vmConfig.getString("isExtendsController")));
        map.put("controllerExtends", vmConfig.getString("controllerExtends"));
        map.put("controllerExtendsSimpleName", vmConfig.getString("controllerExtendsSimpleName"));
        // 公共返回结果类配置
        map.put("commResultFullName", vmConfig.getString("commResultFullName"));
        map.put("commResultSimpleName", vmConfig.getString("commResultSimpleName"));
        // 获取请求用户的工具类
        map.put("getRequestUserFullName", vmConfig.getString("getRequestUserFullName"));
        map.put("getRequestUserSimpleName", vmConfig.getString("getRequestUserSimpleName"));
        map.put("getUserIdMethod", vmConfig.getString("getUserIdMethod"));
        map.put("getUserNicknameMethod", vmConfig.getString("getUserNicknameMethod"));





        // 获取模板列表 渲染生成文件
        VelocityContext context = new VelocityContext(map);
        List<String> templates = loadTemplates();
        for (String template : templates) {
            //渲染模板的输出编写器
            StringWriter sw = new StringWriter();
            // 根据模板路径获取模板
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            // 渲染模板
            tpl.merge(context, sw);

            try {
                zip.putNextEntry(new ZipEntry(Objects.requireNonNull(getFileName(template, table.getClassName(), packages))));
                IOUtils.write(sw.toString(), zip, "UTF-8");
                IOUtils.closeQuietly(sw);
                // 此处只可以关闭 entry 不可关闭整个zip 流
                zip.closeEntry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 读取代码生成器配置
     *
     * @return
     * @throws ConfigurationException
     */
    public static Configuration getGenConfig() throws ConfigurationException {
        return new PropertiesConfiguration("generator.properties");
    }

    /**
     * 表名转换成标准java类
     *
     * @param tableName       表名
     * @param tabPrefix       表前缀
     * @param isAutoRemovePre 是否自动去除表前缀
     * @return
     */
    private static String tableNameToJavaClassName(String tableName, String tabPrefix, String isAutoRemovePre) {
        if (tabPrefix != null && tabPrefix.length() > 0 && Boolean.TRUE.equals(Boolean.valueOf(isAutoRemovePre))) {
            tableName = tableName.substring(tabPrefix.length() + 1);
        }
        return strToJavaClass(tableName);
    }

    /**
     * 下划线转驼峰 列名转换成Java属性名
     * 整个字符串首字母大写 标准java类名
     *
     * @param str 要转驼峰的字符串
     * @return
     */
    private static String strToJavaClass(String str) {
        return WordUtils.capitalizeFully(str, '_').replace("_", "");
    }


    /**
     * 构建文件名称
     *
     * @param template    模板
     * @param className   表对应的类名
     * @param packageName 包名
     * @return 完整的存盘文件名
     */
    private static String getFileName(String template, String className, String packageName) {
        // 包路径 main（maven 的路径）windows文件系统下 main\java\...  标准maven路径
        String packagePath = "main" + File.separator + "java" + File.separator;
        if (packageName != null && packageName.length() > 0) {
            packagePath += packageName.replace(".", File.separator) + File.separator;
        }

        if (template.contains("Domain.java.vm")) {
            return packagePath + "domain" + File.separator + className + "DO.java";
        } else if (template.contains("Controller.java.vm")) {
            return packagePath + "controller" + File.separator + className + "Controller.java";
        } else if (template.contains("ServiceInterface.java.vm")) {
            return packagePath + "service" + File.separator + className + "Service.java";
        } else if (template.contains("ServiceImpl.java.vm")) {
            return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        } else if (template.contains("DaoInterface.java.vm")) {
            return packagePath + "dao" + File.separator + className + "Dao.java";
        } else if (template.contains("DaoMapper.xml.vm")) {
            return "main" + File.separator + "resources" + File.separator + "mapper" + File.separator + className + "Mapper.xml";
        }
        return null;
    }


    /**
     * 判断字符串是否包含有效字符
     *
     * @param str 要校验的字符串
     * @return 不含有效字符 返回 true | 含有有效字符返回 false
     */
    private static boolean strIsEmptyNotTrim(String str) {
        return str == null || str.length() == 0 || org.apache.commons.lang3.StringUtils.trim(str).length() == 0
                || "&nbsp;".equals(str.trim());
    }

    /**
     * 格式化指定日期
     *
     * @param format 格式化样式
     * @param date   待 date 对象
     * @return
     */
    public static String parseDateToStr(final String format, final Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(format).format(date);
    }

}
